
package com.jf.cloud.supplier.service.impl;

import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.cache.constant.SupplierCacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.supplier.constant.SupplierWalletZoneConstant;
import com.jf.cloud.supplier.mapper.SupplierWalletMapper;
import com.jf.cloud.supplier.mapper.SupplierWalletZoneMapper;
import com.jf.cloud.supplier.model.SupplierWalletZone;
import com.jf.cloud.supplier.service.SupplierWalletZoneService;
import com.jf.cloud.supplier.vo.SupplierWalletVO;
import com.jf.cloud.supplier.vo.SupplierWalletZoneInfoVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 
 * @author DOG
 */
@Service
public class SupplierWalletZoneServiceImpl implements SupplierWalletZoneService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SupplierWalletZoneServiceImpl.class);

    @Autowired
    private SupplierWalletZoneMapper supplierWalletZoneMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SupplierWalletMapper supplierWalletMapper;

    @Override
    public void update(SupplierWalletZone supplierWalletZone) {
        supplierWalletZoneMapper.update(supplierWalletZone);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doZone(int needZoneNum, Long supplierId) {
        RLock lock = redissonClient.getLock(LockCacheNames.LOCK_SUPPLIER_WALLET_ZONE_PREFIX + supplierId);
        try {
            lock.lock();

            // 分片前检查分片数量，避免并发导致重复分片
            SupplierWalletVO supplierWallet = supplierWalletMapper.getBySupplierId(supplierId);
            if (Objects.equals(supplierWallet.getZoneNum(), SupplierWalletZoneConstant.ZONE_NUM)) {
                return;
            }

            // 要分成的片数是 n + 1的模式
            // 假设有100块钱，要分成101份，那么向上取整平均每份是100块，最后一个次分片是0
            Long mainSettleAmount = divToRoundUp(supplierWallet.getSettledAmount(), SupplierWalletZoneConstant.ZONE_NUM + 1);

            List<SupplierWalletZone> supplierWalletZones = getSupplierWalletZone(supplierId, SupplierWalletZoneConstant.ZONE_NUM, mainSettleAmount, supplierWallet.getSettledAmount());

            // 插入各个分片
            supplierWalletZoneMapper.batchSave(supplierWalletZones);

            // 更新主分片的结算金额和分片状态
            supplierWalletMapper.updateZoneNum(SupplierWalletZoneConstant.ZONE_NUM, supplierId, mainSettleAmount);
        } finally {
            lock.unlock();
        }
    }

    @Override
    @Cacheable(cacheNames = SupplierCacheNames.SUPPLIER_WALLET_ZONE_INFO_KEY, key = "#supplierId", sync = true)
    public SupplierWalletZoneInfoVO getShopZoneInfo(Long supplierId) {
        return supplierWalletMapper.getSupplierWalletZoneInfo(supplierId);
    }

    @Override
    public void removeZoneCache(List<Long> supplierIds) {
        List<String> keyList = new ArrayList<>();
        for (Long supplierId : supplierIds) {
            keyList.add(SupplierCacheNames.SUPPLIER_WALLET_ZONE_INFO_KEY + CacheNames.UNION + supplierId);
        }
        RedisUtil.deleteBatch(keyList);
    }

    @Override
    public void updateZoneSettleAmount(Long supplierId, Long totalSettleAmount, int zoneNum) {

        Long mainSettleAmount = divToRoundUp(totalSettleAmount, zoneNum + 1);
        // 更新主分片的结算金额和分片状态
        supplierWalletMapper.updateZoneNum(zoneNum, supplierId, mainSettleAmount);

        List<SupplierWalletZone> supplierWalletZoneList = new ArrayList<>();
        // 分成对应片数
        for (int i = 0; i < zoneNum; i++) {
            SupplierWalletZone shopWalletZone = new SupplierWalletZone();
            shopWalletZone.setSupplierId(supplierId);
            shopWalletZone.setZoneNo(i);
            if (totalSettleAmount - mainSettleAmount >= 0) {
                shopWalletZone.setSettledAmount(mainSettleAmount);
                // 减少剩余结算金额
                totalSettleAmount -= mainSettleAmount;
            } else {
                // 把剩下的金额全部给该分片，往后的分片都是0
                shopWalletZone.setSettledAmount(totalSettleAmount);
                totalSettleAmount = 0L;
            }
            supplierWalletZoneList.add(shopWalletZone);
        }

        // 更新店铺分片的结算金额
        supplierWalletZoneMapper.batchUpdate(supplierWalletZoneList);
    }

    private List<SupplierWalletZone> getSupplierWalletZone(Long supplierId, int zoneNum, Long mainSettleAmount, Long totalSettleAmount) {
        List<SupplierWalletZone> supplierWalletZoneList = new ArrayList<>();
        // 分成对应片数
        for (int i = 0; i < zoneNum; i++) {
            SupplierWalletZone supplierWalletZone = new SupplierWalletZone();
            supplierWalletZone.setSupplierId(supplierId);
            supplierWalletZone.setZoneNo(i);
            supplierWalletZone.setUnsettledAmount(0L);
            if (totalSettleAmount - mainSettleAmount >= 0) {
                supplierWalletZone.setSettledAmount(mainSettleAmount);
                // 减少剩余结算金额
                totalSettleAmount -= mainSettleAmount;
            } else {
                // 把剩下的金额全部给该分片，往后的分片都是0
                supplierWalletZone.setSettledAmount(totalSettleAmount);
                totalSettleAmount = 0L;
            }
            supplierWalletZoneList.add(supplierWalletZone);
        }
        return supplierWalletZoneList;
    }

    /**
     * 两个数相除然后向上取整
     * @param a 除数
     * @param b 被除数
     * @return 返回值
     */
    private long divToRoundUp(long a, int b) {
        return new BigDecimal(a).divide(new BigDecimal(b),0, RoundingMode.UP).longValue();
    }
}
